﻿/**
 *	\file		lxpref_route.cxx
 *	\date		(2014-12-07 23:52:50)/(2014-12-07 23:52:50)
 *-----------------------------------------------------------------------------
 *	\brief
 *	\version	1.0.0.1
 *	\author		Nick Shallery	(nicknide@gmail.com)
 *	\copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#include	"./lxpref_route.hxx"
#include	"./lxpref_route_clt_usr.hxx"

#include	"./lxpref.h"

#include	"./lxpref_route_msg.hxx"

#include	"../lxsamsara/recurrence.h"

#include	<lxnet/lxnet_itf.h>
#include	<lolix/loli/critical_section.h>
#include	<lolix/loli/file.h>
#include	<lolix/loli/thread.h>
#include	<lolix/toy/archive.hpp>
#define		LOLIX_DEF__MEMORY_FILE_STREAM
#include	<lolix/toy/archive_file_filter.hpp>


lxpref::_inst::lxpref_route_inst::lxpref_route_inst(lolix::dynamic_allocator* alc)
	: _td_view(0)
	, _net_listen(0)
	//, _bind_path(0)
	, _net_conns(lolix::toy::less(), alc)
{
}


lxpref::_inst::lxpref_route_inst::~lxpref_route_inst(void)
{
	_recur->release();
	this->end();

	_async_fac->release();
	_td_fac->release();
	_net_fac->release();
	//if ( _net_listen )
	//	_net_listen->release();

	_td_view->safe_release();
	//if ( _bind_path )
	//	_prf->free_string(_bind_path);

	for ( auto iter = _net_conns.begin(); iter != _net_conns.end(); ++iter )
		iter->first->close();
	_net_conns.clear();

	_cs_for_add_net_conn->release();
	_stdc->release();
	_prf->release();
	_alc->release();
}


lolix::LOLIX_RETNV
lxpref::_inst::lxpref_route_inst::create_inst(this_type** out_ptr, lolix::dynamic_allocator* alc, lolix::ref_factory* ref_fac
									, lxprofile* pref
									, lolix::loli::critical_section_factory* cs_fac
									, lolix::loli::thread_factory* td_fac, lxnet::net_factory* net_fac
									, lolix::toy::stdc_fun* stdc
									, lxsamsara::recurrence* recur
									, lxio::file_async_pool_factory* async_fac)
{
	LOLIX_RETNV rtv;
	lolix::loli::critical_section* cs;
	if ( LOLIX_CHECK_ERROR(rtv = cs_fac->create_inst(&cs LOLIX_CS_MARK_NAME_P(L"lxpref_route_inst::_cs_for_add_net_conn"))) )
		return rtv;
	atexit_release(*cs);
	this_type* ptr;
	if ( LOLIX_CHECK_ERROR(rtv = __Create_inst_set_alc(&ptr, alc, ref_fac, alc)) )
		return rtv;
	(ptr->_stdc = stdc)->inc_ref();
	(ptr->_td_fac = td_fac)->inc_ref();
	(ptr->_net_fac = net_fac)->inc_ref();
	(ptr->_prf = pref)->inc_ref();
	(ptr->_async_fac = async_fac)->inc_ref();
	(ptr->_cs_for_add_net_conn = cs)->inc_ref();
	(ptr->_recur = recur)->inc_ref();
	*out_ptr = ptr;
	return rtv;
}


lolix::LOLIX_RETNV
lxpref::_inst::lxpref_route_inst::start(lolix::lx_wchar const* path, lolix::size_type path_len, lxio::file_async_pool* async_pool)
{
	if ( this->_td_view )
		return lolix::LOLIX_ERROR_BAD_REDO;

	//if ( this->_bind_path )
	//	this->_prf->free_string(this->_bind_path);
	//this->_bind_path = this->_prf->new_string(path);

	LOLIX_RETNV rtv;
	if ( LOLIX_CHECK_ERROR(rtv = __Start_conn(path, path_len, async_pool)) )
		return rtv;

	lolix::loli::thread* td;
	if ( LOLIX_CHECK_ERROR(rtv = this->_td_fac->create_inst(&td)) )
		return rtv;
	atexit_release(*td);

	_need_exists_thread = 0;
	lolix::loli::thread::FN_INVOKE_PROC td_proc(this, &lxpref_route_inst::__Fetch_data_thread);
	if ( LOLIX_CHECK_ERROR(rtv = td->start(td->THREAD_START_NORMAL, td_proc, true)) )
		return rtv;

	(this->_td_view = td)->inc_ref();
	return rtv;
}


lolix::LOLIX_RETNV
lxpref::_inst::lxpref_route_inst::end(void)
{
	if ( !this->_td_view )
		return lolix::LOLIX_WARNING_INVALID_STATE;
	_need_exists_thread = 1;
	lolix::loli::signal* sign;
	LOLIX_RETNV rtv;
	if ( !LOLIX_CHECK_ERROR(rtv = this->_prf->query_signal(&sign)) )
	{
		sign->set();
		sign->release();
	}

	if ( LOLIX_CHECK_ERROR(rtv = this->_td_view->wait(0, lolix::toy::time_span::time_max())) )
	{
		_need_exists_thread = 0;
		return rtv;
	}

	//this->_prf->free_string(this->_bind_path);
	//this->_bind_path = 0;
	this->_td_view->release();
	this->_td_view = 0;
	return rtv;
}


lolix::LOLIX_RETNV
lxpref::_inst::lxpref_route_inst::__Start_conn(lolix::lx_wchar const* path, lolix::size_type path_len, lxio::file_async_pool* async_pool)
{
	lxnet::net_listen* nl;
	LOLIX_RETNV rtv;
	if ( LOLIX_CHECK_ERROR(rtv = this->_net_fac->query_listen(&nl, path, path_len, async_pool)) )
		return rtv;
	atexit_release(*nl);
	(this->_net_listen = nl)->inc_ref();
	nl->on_conn(&lxpref_route_inst::__On_conn, this, 0, 0);
	return lolix::LOLIX_RETNV_OK;
}


lolix::LOLIX_RETNV
lxpref::_inst::lxpref_route_inst::__Fetch_data_thread(lolix::loli::thread* td)
{
	if ( this->_need_exists_thread )
		return lolix::LOLIX_RETNV_OK;

	LOLIX_RETNV rtv;
	lolix::loli::signal* sign;
	if ( LOLIX_CHECK_ERROR(rtv = this->_prf->query_signal(&sign)) )
		return rtv;
	atexit_release(*sign);

	if ( this->_need_exists_thread )
		return lolix::LOLIX_RETNV_OK;

	while ( !LOLIX_CHECK_ERROR(rtv = sign->wait(lolix::toy::time_span::time_max())) )
	{
		if ( this->_need_exists_thread )
			break;

		lolix::toy::list<lolix::toy::loli_ptr<lolix::loli::file_async>, allocator_type> fas(this->_alc);
		{
			bind_invoke(*this->_cs_for_add_net_conn, enter(), leave());
			for ( auto iter = this->_net_conns.begin(); iter != this->_net_conns.end(); ++iter )
				fas.push_back(iter->first.get());
		}

		if ( fas.empty() )
			continue;

		event_info* evt_inf;
		lolix::loli::memory_file_stream<allocator_type> mfile(this->_stdc, this->_alc);
		lolix::size_type last_file_size = 0;
		lolix::toy::archive_no_throw arc(&mfile);
		while ( !LOLIX_IS_ERROR(this->_prf->peek_node(&evt_inf)) )
		{
			if ( !evt_inf )
				break;
			message::packet_header<allocator_type> pct_header(message::MSG_TYPE_INFO, this->_alc);
			arc << *evt_inf;
			this->_prf->free_node(evt_inf);
			lolix::lx_u8 const* start = static_cast<lolix::lx_u8 const*>(mfile.dates()) + last_file_size;
			pct_header.dat.assign(start, start + mfile.size() - last_file_size);
			mfile.seek_to(last_file_size);
			arc << pct_header;
			lolix::lx_i64 pos = 0;
			mfile.seek(&pos, 1);
			last_file_size = static_cast<lolix::size_type>(pos);
		}

		if ( !last_file_size )
			continue;

		for ( auto iter = fas.begin(); iter != fas.end(); ++iter )
			(*iter)->write(0, 0, last_file_size, mfile.dates(), 0, 0);
	}
	_net_listen->release();
	_net_listen = 0;
	return rtv;
}


void
lxpref::_inst::lxpref_route_inst::__On_conn(lxnet::net_connect* net_conn, void* usr_ptr)
{
	this_type* ptr = static_cast<this_type*>(usr_ptr);
	lolix::loli::file_async* fa;
	if ( LOLIX_CHECK_ERROR(net_conn->query_itf(&fa)) )
		return;
	atexit_release(*fa);

	clt_usr* clt;
	if ( LOLIX_CHECK_ERROR(clt_usr::create_inst(&clt, ptr->_alc, 0, ptr->_stdc, fa, ptr->_recur)) )
		return;

	decltype(ptr->_net_conns.begin()) iter;
	{
		bind_invoke(*ptr->_cs_for_add_net_conn, enter(), leave());
		iter = ptr->_net_conns.insert(lolix::toy::make_pair(fa, clt)).first;
	}
	void* buf;
	size_type buf_size;
	clt->_get_recv_buf(&buf, &buf_size);
	if ( LOLIX_CHECK_ERROR(fa->read(0, 0, buf_size, buf, clt, ptr, &lxpref_route_inst::__On_read)) )
	{
		atexit_release(*clt);
		bind_invoke(*ptr->_cs_for_add_net_conn, enter(), leave());
		ptr->_net_conns.erase(iter);
	}
}


void
lxpref::_inst::lxpref_route_inst::__On_recvMsg_s(clt_usr* cusr, message::PREF_MSG_TYPE msgType, void const* buf, lolix::size_type buf_size, void* usr_ptr)
{
	this_type* ptr = static_cast<this_type*>(usr_ptr);
	ptr->__On_recvMsg(cusr, msgType, buf, buf_size);
}


void
lxpref::_inst::lxpref_route_inst::__On_recvMsg(clt_usr* cusr, message::PREF_MSG_TYPE msgType, void const* buf, lolix::size_type buf_size)
{
	switch ( msgType )
	{
	case message::MSG_TYPE_QUERY_NAME:
		{
			message::query_names<allocator_type> qn(this->_alc);
			lolix::loli::memory_file_stream<allocator_type> mfs(this->_stdc, this->_alc);
			mfs.write(buf, &buf_size);
			mfs.seek_to(0);
			lolix::toy::archive_no_throw arc(&mfs);
			arc >> qn;

			message::response_names<allocator_type> rn(this->_alc);
			for ( auto iter = qn.names_address.begin(); iter != qn.names_address.end(); ++iter )
			{
				lolix::lx_wchar const* name = (lolix::lx_wchar const*)(lolix::size_type)*iter;
				size_type name_len = this->_stdc->wcslen(name);
				rn.name_ptr.push_back(*iter);
				rn.name_len.push_back(name_len);
				rn.name_idx.push_back(rn.names.size());
				rn.names.insert(rn.names.end(), name, name + name_len);
			}
			mfs.seek_to(0);
			arc << rn;
			message::packet_header<allocator_type> pheader(this->_alc);
			mfs.swap_datas(pheader.dat, 0);
			mfs.seek_to(0);
			pheader.msg_type = message::MSG_TYPE_QUEST_NAME;
			arc << pheader;
			lolix::lx_i64 len = 0;
			mfs.seek(&len, 1);
			lolix::loli::file_async* fasync = cusr->_get_file_async();
			fasync->write(0, 0, len, mfs.dates(), 0, 0);
		}

		break;
	}
}


void
lxpref::_inst::lxpref_route_inst::__On_read(lolix::loli::file_async_result* far)
{
	clt_usr* clt = static_cast<clt_usr*>(far->usr_dat());
	lolix::loli::file_async* fa = clt->_get_file_async();
	LOLIX_DEF__ENTER_SECTION();
	lolix::LOLIX_RETNV rtv;
	lolix::size_type dat_size;
	lolix::lx_u8* result;
	if ( LOLIX_CHECK_ERROR(rtv = far->get_result((void**)&result, &dat_size)) || !dat_size )
		break;

	clt->_do_recv(dat_size, &lxpref_route_inst::__On_recvMsg_s, this);
	clt->_get_recv_buf((void**)result, &dat_size);

	if ( LOLIX_CHECK_ERROR(rtv = fa->read(0, 0, dat_size, result, clt, this, &lxpref_route_inst::__On_read)) )
		break;
	return;
	LOLIX_DEF__LEAVE_SECTION();

	atexit_release(*clt);
	bind_invoke(*this->_cs_for_add_net_conn, enter(), leave());
	this->_net_conns.erase(fa);
}
